feat: migrate to python for data validation (#1214)
* feat: migrate to python for data validation Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: address PR comments Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: add unused kwargs to validate functions Signed-off-by: Devin Buhl <devin@buhl.casa> * chore: update renovate pip and ansible regex Signed-off-by: Devin Buhl <devin@buhl.casa> * feat: add bootstrap_nodes test Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: update taskfiles Signed-off-by: Devin Buhl <devin@buhl.casa> --------- Signed-off-by: Devin Buhl <devin@buhl.casa>
This commit is contained in:
1 parent
7456164df0
commit
b89b7b3ed9
33 files changed
+338
-535
No files matched your search
+1
-5
@@ -9,10 +9,6 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[Makefile]
|
[*.{bash,py,sh}]
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.{bash,sh}]
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
@@ -36,6 +36,16 @@
|
|||||||
"(^|/)kustomization\\.ya?ml(\\.j2)?(\\.j2)?$"
|
"(^|/)kustomization\\.ya?ml(\\.j2)?(\\.j2)?$"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"pip_requirements": {
|
||||||
|
"fileMatch": [
|
||||||
|
"(^|/)[\\w-]*requirements(-\\w+)?\\.(txt|pip)(\\.j2)?(\\.j2)?$"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ansible-galaxy": {
|
||||||
|
"fileMatch": [
|
||||||
|
"(^|/)(galaxy|requirements)(\\.ansible)?\\.ya?ml(\\.j2)?(\\.j2)?$"
|
||||||
|
]
|
||||||
|
},
|
||||||
// commit message topics
|
// commit message topics
|
||||||
"commitMessageTopic": "{{depName}}",
|
"commitMessageTopic": "{{depName}}",
|
||||||
"commitMessageExtra": "to {{newVersion}}",
|
"commitMessageExtra": "to {{newVersion}}",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
ci_test: true
|
skip_tests: true
|
||||||
|
|
||||||
bootstrap_distribution: k0s
|
bootstrap_distribution: k0s
|
||||||
bootstrap_github_username: onedr0p
|
bootstrap_github_username: onedr0p
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
ci_test: true
|
skip_tests: true
|
||||||
|
|
||||||
bootstrap_distribution: k3s
|
bootstrap_distribution: k3s
|
||||||
bootstrap_github_username: onedr0p
|
bootstrap_github_username: onedr0p
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
ci_test: true
|
skip_tests: true
|
||||||
|
|
||||||
bootstrap_distribution: k3s
|
bootstrap_distribution: k3s
|
||||||
bootstrap_github_username: onedr0p
|
bootstrap_github_username: onedr0p
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
ci_test: true
|
skip_tests: true
|
||||||
|
|
||||||
bootstrap_distribution: k3s
|
bootstrap_distribution: k3s
|
||||||
bootstrap_github_username: onedr0p
|
bootstrap_github_username: onedr0p
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
ci_test: true
|
skip_tests: true
|
||||||
|
|
||||||
bootstrap_distribution: talos
|
bootstrap_distribution: talos
|
||||||
bootstrap_github_username: onedr0p
|
bootstrap_github_username: onedr0p
|
||||||
|
|||||||
+13
-15
@@ -77,7 +77,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: brew install go-task
|
run: brew install go-task
|
||||||
|
|
||||||
- name: Install Brew dependencies
|
- name: Run Workstation tasks
|
||||||
if: ${{ github.event_name == 'pull_request' && steps.cache-homebrew-packages.outputs.cache-hit != 'true' }}
|
if: ${{ github.event_name == 'pull_request' && steps.cache-homebrew-packages.outputs.cache-hit != 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: task workstation:brew
|
run: task workstation:brew
|
||||||
@@ -86,15 +86,15 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: direnv allow .
|
run: direnv allow .
|
||||||
|
|
||||||
- name: Initialize Sops Age key
|
- name: Run Sops Age key task
|
||||||
shell: bash
|
shell: bash
|
||||||
run: task sops:age-keygen
|
run: task sops:age-keygen
|
||||||
|
|
||||||
- name: Install Ansible dependencies
|
- name: Run setup-virtual-env task
|
||||||
shell: bash
|
shell: bash
|
||||||
run: task ansible:deps force=false
|
run: task setup-virtual-env
|
||||||
|
|
||||||
- name: Generate bootstrap config file
|
- name: Run init tasks
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
task init
|
task init
|
||||||
@@ -109,28 +109,26 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "distribution=$(yq '.bootstrap_distribution' ./bootstrap/vars/config.yaml)" >> $GITHUB_OUTPUT
|
echo "distribution=$(yq '.bootstrap_distribution' ./bootstrap/vars/config.yaml)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Run configure
|
- name: Run configure task
|
||||||
shell: bash
|
shell: bash
|
||||||
run: task configure --yes
|
run: task configure --yes
|
||||||
|
|
||||||
- name: Run Talos configure
|
- name: Run Talos tasks
|
||||||
if: ${{ steps.config-env.outputs.distribution == 'talos' }}
|
if: ${{ steps.config-env.outputs.distribution == 'talos' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
task talos:gensecret --yes
|
task talos:gensecret --yes
|
||||||
task talos:genconfig
|
task talos:genconfig
|
||||||
|
|
||||||
- name: Run Ansible lint
|
- name: Run Ansible tasks
|
||||||
if: ${{ steps.config-env.outputs.distribution == 'k3s' || steps.config-env.outputs.distribution == 'k0s' }}
|
if: ${{ steps.config-env.outputs.distribution == 'k3s' || steps.config-env.outputs.distribution == 'k0s' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: task ansible:lint
|
run: |
|
||||||
|
task ansible:deps force=false
|
||||||
|
task ansible:lint
|
||||||
|
task ansible:list
|
||||||
|
|
||||||
- name: List Hosts with Ansible
|
- name: Run repo clean and reset tasks
|
||||||
if: ${{ steps.config-env.outputs.distribution == 'k3s' || steps.config-env.outputs.distribution == 'k0s' }}
|
|
||||||
shell: bash
|
|
||||||
run: task ansible:list
|
|
||||||
|
|
||||||
- name: Run repo clean and reset
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
task repository:clean
|
task repository:clean
|
||||||
|
|||||||
@@ -14,15 +14,26 @@ env:
|
|||||||
vars:
|
vars:
|
||||||
ANSIBLE_LINT_FILE: "{{.ANSIBLE_DIR}}/.ansible-lint"
|
ANSIBLE_LINT_FILE: "{{.ANSIBLE_DIR}}/.ansible-lint"
|
||||||
ANSIBLE_INVENTORY_FILE: "{{.ANSIBLE_DIR}}/inventory/hosts.yaml"
|
ANSIBLE_INVENTORY_FILE: "{{.ANSIBLE_DIR}}/inventory/hosts.yaml"
|
||||||
ANSIBLE_REQUIREMENTS_FILE: "{{.ROOT_DIR}}/requirements.yaml"
|
ANSIBLE_REQUIREMENTS_FILE: "{{.ANSIBLE_DIR}}/requirements.yaml"
|
||||||
PIP_REQUIREMENTS_FILE: "{{.ROOT_DIR}}/requirements.txt"
|
ANSIBLE_PIP_REQUIREMENTS_FILE: "{{.ANSIBLE_DIR}}/requirements.txt"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
desc: Set up Ansible dependencies for the environment
|
desc: Set up Ansible dependencies
|
||||||
cmds:
|
cmds:
|
||||||
- task: .setup-virtual-env
|
- task: :setup-virtual-env
|
||||||
|
- .venv/bin/python3 -m pip install --upgrade --requirement "{{.ANSIBLE_PIP_REQUIREMENTS_FILE}}"
|
||||||
|
- .venv/bin/ansible-galaxy install --role-file "{{.ANSIBLE_REQUIREMENTS_FILE}}" {{if eq .force "true"}}--force{{end}}
|
||||||
|
preconditions:
|
||||||
|
- { msg: "Missing Ansible requirements file", sh: "test -f {{.ANSIBLE_REQUIREMENTS_FILE}}" }
|
||||||
|
- { msg: "Missing Pip requirements file", sh: "test -f {{.ANSIBLE_PIP_REQUIREMENTS_FILE}}" }
|
||||||
|
sources:
|
||||||
|
- "{{.ANSIBLE_REQUIREMENTS_FILE}}"
|
||||||
|
- "{{.ANSIBLE_PIP_REQUIREMENTS_FILE}}"
|
||||||
|
generates:
|
||||||
|
- "{{.VIRTUAL_ENV}}/bin/ansible"
|
||||||
|
- "{{.VIRTUAL_ENV}}/bin/ansible-galaxy"
|
||||||
vars:
|
vars:
|
||||||
force: '{{.force | default "true"}}'
|
force: '{{.force | default "true"}}'
|
||||||
|
|
||||||
@@ -74,24 +85,6 @@ tasks:
|
|||||||
preconditions:
|
preconditions:
|
||||||
- { msg: "Missing Ansible lint file", sh: "test -f {{.ANSIBLE_LINT_FILE}}" }
|
- { msg: "Missing Ansible lint file", sh: "test -f {{.ANSIBLE_LINT_FILE}}" }
|
||||||
|
|
||||||
.setup-virtual-env:
|
|
||||||
internal: true
|
|
||||||
cmds:
|
|
||||||
- "{{.PYTHON_BIN}} -m venv {{.ROOT_DIR}}/.venv"
|
|
||||||
- .venv/bin/python3 -m pip install --upgrade pip setuptools wheel
|
|
||||||
- .venv/bin/python3 -m pip install --upgrade --requirement "{{.PIP_REQUIREMENTS_FILE}}"
|
|
||||||
- .venv/bin/ansible-galaxy install --role-file "{{.ANSIBLE_REQUIREMENTS_FILE}}" {{if eq .force "true"}}--force{{end}}
|
|
||||||
sources:
|
|
||||||
- "{{.PIP_REQUIREMENTS_FILE}}"
|
|
||||||
- "{{.ANSIBLE_REQUIREMENTS_FILE}}"
|
|
||||||
generates:
|
|
||||||
- "{{.ROOT_DIR}}/.venv/pyvenv.cfg"
|
|
||||||
preconditions:
|
|
||||||
- { msg: "Missing Ansible requirements file", sh: "test -f {{.ANSIBLE_REQUIREMENTS_FILE}}" }
|
|
||||||
- { msg: "Missing Pip requirements file", sh: "test -f {{.PIP_REQUIREMENTS_FILE}}" }
|
|
||||||
vars:
|
|
||||||
force: '{{.force | default "true"}}'
|
|
||||||
|
|
||||||
.reset:
|
.reset:
|
||||||
internal: true
|
internal: true
|
||||||
cmd: rm -rf {{.ANSIBLE_DIR}}
|
cmd: rm -rf {{.ANSIBLE_DIR}}
|
||||||
@@ -46,7 +46,7 @@ If you are marching forward, now is a good time to choose whether you will deplo
|
|||||||
### System requirements
|
### System requirements
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> 1. The included behaviour of Talos, k0s or k3s is that all nodes are able to run workloads, **including** control nodes. Worker nodes are therefore optional.
|
> 1. The included behaviour of Talos, k3s or k0s is that all nodes are able to run workloads, **including** control nodes. Worker nodes are therefore optional.
|
||||||
> 2. Do you have 3 or more nodes? It is strongly recommended to make 3 of them control nodes for a highly available control plane.
|
> 2. Do you have 3 or more nodes? It is strongly recommended to make 3 of them control nodes for a highly available control plane.
|
||||||
> 3. Running the cluster on Proxmox VE? My thoughts and recommendations about that are documented [here](https://onedr0p.github.io/home-ops/notes/proxmox-considerations.html).
|
> 3. Running the cluster on Proxmox VE? My thoughts and recommendations about that are documented [here](https://onedr0p.github.io/home-ops/notes/proxmox-considerations.html).
|
||||||
|
|
||||||
@@ -193,12 +193,12 @@ Once you have installed Talos or Debian on your nodes, there are six stages to g
|
|||||||
go-task workstation:yay
|
go-task workstation:yay
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Setup a Python virual env and install Ansible by running the following task command.
|
4. Setup a Python virual env by running the following task command.
|
||||||
|
|
||||||
📍 _This commands requires Python 3.11+ to be installed._
|
📍 _This commands requires Python 3.11+ to be installed._
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
task ansible:deps
|
task setup-virtual-env
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Continue on to 🔧 [**Stage 3**](#-stage-3-do-bootstrap-configuration)
|
5. Continue on to 🔧 [**Stage 3**](#-stage-3-do-bootstrap-configuration)
|
||||||
@@ -278,14 +278,7 @@ Once you have installed Talos or Debian on your nodes, there are six stages to g
|
|||||||
|
|
||||||
7. Once done run the following command which will verify and generate all the files needed to continue.
|
7. Once done run the following command which will verify and generate all the files needed to continue.
|
||||||
|
|
||||||
> [!NOTE]
|
📍 _The following configure task will create a `./ansible` directory for k3s or k0s and the following directories under `./kubernetes` for all distributions_
|
||||||
> The following configure task will create a `./ansible` directory for k3s or k0s and the following directories under `./kubernetes`.
|
|
||||||
> ```sh
|
|
||||||
> 📁 kubernetes # Kubernetes cluster defined as code
|
|
||||||
> ├─📁 bootstrap # Flux installation (not tracked by Flux)
|
|
||||||
> ├─📁 flux # Main Flux configuration of repository
|
|
||||||
> └─📁 apps # Apps deployed into the cluster grouped by namespace
|
|
||||||
> ```
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
task configure
|
task configure
|
||||||
@@ -305,25 +298,31 @@ Once you have installed Talos or Debian on your nodes, there are six stages to g
|
|||||||
|
|
||||||
1. Ensure you are able to SSH into your nodes from your workstation using a private SSH key **without a passphrase** (for example using a SSH agent). This lets Ansible interact with your nodes.
|
1. Ensure you are able to SSH into your nodes from your workstation using a private SSH key **without a passphrase** (for example using a SSH agent). This lets Ansible interact with your nodes.
|
||||||
|
|
||||||
2. Verify Ansible can view your config
|
3. Install the Ansible dependencies
|
||||||
|
|
||||||
|
```sh
|
||||||
|
task ansible:deps
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Verify Ansible can view your config
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
task ansible:list
|
task ansible:list
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Verify Ansible can ping your nodes
|
5. Verify Ansible can ping your nodes
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
task ansible:ping
|
task ansible:ping
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Run the Ansible prepare playbook (nodes wil reboot when done)
|
6. Run the Ansible prepare playbook (nodes wil reboot when done)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
task ansible:run playbook=cluster-prepare
|
task ansible:run playbook=cluster-prepare
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Continue on to ⛵ [**Stage 5**](#-stage-5-install-kubernetes)
|
7. Continue on to ⛵ [**Stage 5**](#-stage-5-install-kubernetes)
|
||||||
|
|
||||||
### ⛵ Stage 5: Install Kubernetes
|
### ⛵ Stage 5: Install Kubernetes
|
||||||
|
|
||||||
|
|||||||
+19
-19
@@ -13,7 +13,7 @@ vars:
|
|||||||
BOOTSTRAP_CONFIG_FILE: "{{.BOOTSTRAP_DIR}}/vars/config.yaml"
|
BOOTSTRAP_CONFIG_FILE: "{{.BOOTSTRAP_DIR}}/vars/config.yaml"
|
||||||
KUBECONFIG_FILE: "{{.ROOT_DIR}}/kubeconfig"
|
KUBECONFIG_FILE: "{{.ROOT_DIR}}/kubeconfig"
|
||||||
MAKEJINJA_CONFIG_FILE: "{{.ROOT_DIR}}/makejinja.toml"
|
MAKEJINJA_CONFIG_FILE: "{{.ROOT_DIR}}/makejinja.toml"
|
||||||
PYTHON_VERSION_CHECK_FILE: "{{.BOOTSTRAP_DIR}}/scripts/py_version_check.py"
|
PIP_REQUIREMENTS_FILE: "{{.ROOT_DIR}}/requirements.txt"
|
||||||
SOPS_AGE_FILE: "{{.ROOT_DIR}}/age.key"
|
SOPS_AGE_FILE: "{{.ROOT_DIR}}/age.key"
|
||||||
# Binaries
|
# Binaries
|
||||||
PYTHON_BIN: python3
|
PYTHON_BIN: python3
|
||||||
@@ -37,8 +37,9 @@ tasks:
|
|||||||
default: task -l
|
default: task -l
|
||||||
|
|
||||||
init:
|
init:
|
||||||
desc: Initialize files and directories
|
desc: Initialize virtual env and configuration files
|
||||||
cmds:
|
cmds:
|
||||||
|
- task: setup-virtual-env
|
||||||
- mkdir -p {{.PRIVATE_DIR}}
|
- mkdir -p {{.PRIVATE_DIR}}
|
||||||
- cp -n {{.BOOTSTRAP_ADDONS_FILE | replace ".yaml" ".sample.yaml"}} {{.BOOTSTRAP_ADDONS_FILE}}
|
- cp -n {{.BOOTSTRAP_ADDONS_FILE | replace ".yaml" ".sample.yaml"}} {{.BOOTSTRAP_ADDONS_FILE}}
|
||||||
- cp -n {{.BOOTSTRAP_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.BOOTSTRAP_CONFIG_FILE}}
|
- cp -n {{.BOOTSTRAP_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.BOOTSTRAP_CONFIG_FILE}}
|
||||||
@@ -50,30 +51,13 @@ tasks:
|
|||||||
silent: true
|
silent: true
|
||||||
- cmd: echo {{.BOOTSTRAP_ADDONS_FILE}}
|
- cmd: echo {{.BOOTSTRAP_ADDONS_FILE}}
|
||||||
silent: true
|
silent: true
|
||||||
status:
|
|
||||||
- test -f {{.BOOTSTRAP_ADDONS_FILE}}
|
|
||||||
- test -f {{.BOOTSTRAP_CONFIG_FILE}}
|
|
||||||
|
|
||||||
configure:
|
configure:
|
||||||
desc: Configure repository from Ansible vars
|
desc: Configure repository from Ansible vars
|
||||||
prompt: Any conflicting config in the root kubernetes and ansible directories will be overwritten... continue?
|
prompt: Any conflicting config in the root kubernetes and ansible directories will be overwritten... continue?
|
||||||
cmds:
|
cmds:
|
||||||
- task: .pre-validate
|
|
||||||
- task: .template
|
- task: .template
|
||||||
- task: .post-validate
|
- task: .post-validate
|
||||||
preconditions:
|
|
||||||
- { msg: "Missing Python version check script", sh: "test -f {{.PYTHON_VERSION_CHECK_FILE}}" }
|
|
||||||
- { msg: "Python version must be 3.11 or greater", sh: "{{.PYTHON_BIN}} {{.PYTHON_VERSION_CHECK_FILE}}" }
|
|
||||||
|
|
||||||
.pre-validate:
|
|
||||||
internal: true
|
|
||||||
cmd: ./.venv/bin/ansible-playbook {{.BOOTSTRAP_DIR}}/validate.yaml
|
|
||||||
env:
|
|
||||||
ANSIBLE_DISPLAY_SKIPPED_HOSTS: "false"
|
|
||||||
preconditions:
|
|
||||||
- { msg: "Missing virtual environment", sh: "test -d {{.ROOT_DIR}}/.venv" }
|
|
||||||
- { msg: "Missing bootstrap addons file", sh: "test -f {{.BOOTSTRAP_ADDONS_FILE}}" }
|
|
||||||
- { msg: "Missing bootstrap config file", sh: "test -f {{.BOOTSTRAP_CONFIG_FILE}}" }
|
|
||||||
|
|
||||||
.template:
|
.template:
|
||||||
internal: true
|
internal: true
|
||||||
@@ -81,10 +65,26 @@ tasks:
|
|||||||
- ./.venv/bin/makejinja
|
- ./.venv/bin/makejinja
|
||||||
- task: sops:encrypt
|
- task: sops:encrypt
|
||||||
preconditions:
|
preconditions:
|
||||||
|
- { msg: "Missing virtual environment", sh: "test -d {{.ROOT_DIR}}/.venv" }
|
||||||
- { msg: "Missing Makejinja config file", sh: "test -f {{.MAKEJINJA_CONFIG_FILE}}" }
|
- { msg: "Missing Makejinja config file", sh: "test -f {{.MAKEJINJA_CONFIG_FILE}}" }
|
||||||
- { msg: "Missing Makejinja loader file", sh: "test -f {{.BOOTSTRAP_DIR}}/scripts/loader.py" }
|
- { msg: "Missing Makejinja loader file", sh: "test -f {{.BOOTSTRAP_DIR}}/scripts/loader.py" }
|
||||||
|
- { msg: "Missing bootstrap addons file", sh: "test -f {{.BOOTSTRAP_ADDONS_FILE}}" }
|
||||||
|
- { msg: "Missing bootstrap config file", sh: "test -f {{.BOOTSTRAP_CONFIG_FILE}}" }
|
||||||
|
|
||||||
.post-validate:
|
.post-validate:
|
||||||
internal: true
|
internal: true
|
||||||
cmds:
|
cmds:
|
||||||
- task: kubernetes:kubeconform
|
- task: kubernetes:kubeconform
|
||||||
|
|
||||||
|
setup-virtual-env:
|
||||||
|
desc: Set up virtual environment
|
||||||
|
cmds:
|
||||||
|
- "{{.PYTHON_BIN}} -m venv {{.ROOT_DIR}}/.venv"
|
||||||
|
- .venv/bin/python3 -m pip install --upgrade pip setuptools wheel
|
||||||
|
- .venv/bin/python3 -m pip install --upgrade --requirement "{{.PIP_REQUIREMENTS_FILE}}"
|
||||||
|
sources:
|
||||||
|
- "{{.PIP_REQUIREMENTS_FILE}}"
|
||||||
|
generates:
|
||||||
|
- "{{.ROOT_DIR}}/.venv/pyvenv.cfg"
|
||||||
|
preconditions:
|
||||||
|
- { msg: "Missing Pip requirements file", sh: "test -f {{.PIP_REQUIREMENTS_FILE}}" }
|
||||||
@@ -1,21 +1,40 @@
|
|||||||
import netaddr, bcrypt
|
import bcrypt
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
import validation
|
||||||
|
|
||||||
def nthhost(value: str, query: int) -> str:
|
def nthhost(value: str, query: int) -> str:
|
||||||
value = netaddr.IPNetwork(value)
|
value = netaddr.IPNetwork(value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nth = int(query)
|
nth = int(query)
|
||||||
if value.size > nth:
|
if value.size > nth:
|
||||||
return str(value[nth])
|
return str(value[nth])
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def encrypt(value: str) -> str:
|
def encrypt(value: str) -> str:
|
||||||
return bcrypt.hashpw(value.encode(), bcrypt.gensalt(rounds=10)).decode("ascii")
|
return bcrypt.hashpw(value.encode(), bcrypt.gensalt(rounds=10)).decode("ascii")
|
||||||
|
|
||||||
class Loader:
|
class Loader:
|
||||||
|
def __init__(self, data):
|
||||||
|
if data.get("skip_tests", False):
|
||||||
|
return
|
||||||
|
validation.validate_python_version()
|
||||||
|
validation.validate_cli_tools(data)
|
||||||
|
validation.validate_distribution(data)
|
||||||
|
validation.validate_github(data)
|
||||||
|
validation.validate_age(data)
|
||||||
|
validation.validate_timezone(data)
|
||||||
|
validation.validate_acme_email(data)
|
||||||
|
validation.validate_flux_github_webhook_token(data)
|
||||||
|
validation.validate_cloudflare(data)
|
||||||
|
validation.validate_host_network(data)
|
||||||
|
validation.validate_bootstrap_dns_server(data)
|
||||||
|
validation.validate_cilium_loadbalancer_mode(data)
|
||||||
|
validation.validate_local_storage_path(data)
|
||||||
|
validation.validate_cluster_cidrs(data)
|
||||||
|
validation.validate_nodes(data)
|
||||||
|
|
||||||
def filters(self):
|
def filters(self):
|
||||||
return [nthhost, encrypt]
|
return [nthhost, encrypt]
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
required_version = (3, 11, 0)
|
|
||||||
|
|
||||||
if sys.version_info >= required_version:
|
|
||||||
print(f"Python version is greater than or equal to 3.11")
|
|
||||||
else:
|
|
||||||
print("Python version is below 3.11. Please upgrade.")
|
|
||||||
exit(1)
|
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
from email_validator import validate_email, EmailNotValidError
|
||||||
|
from functools import wraps
|
||||||
|
from pathvalidate import is_valid_filepath, ValidationError
|
||||||
|
from shutil import which
|
||||||
|
from typing import Callable, Optional
|
||||||
|
from zoneinfo import available_timezones
|
||||||
|
import CloudFlare
|
||||||
|
import netaddr
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
DISTRIBUTIONS = ["k0s", "k3s", "talos"]
|
||||||
|
CILIUM_LOADBALANCER_MODES = ["dsr", "snat"]
|
||||||
|
GLOBAL_CLI_TOOLS = ["age", "cloudflared", "flux", "sops", "jq", "kubeconform", "kustomize"]
|
||||||
|
TALOS_CLI_TOOLS = ["talosctl", "talhelper"]
|
||||||
|
K0S_CLI_TOOLS = ["k0sctl"]
|
||||||
|
|
||||||
|
def required(*keys: str):
|
||||||
|
def wrapper_outter(func: Callable):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(data: dict, *args, **kwargs) -> None:
|
||||||
|
for key in keys:
|
||||||
|
if data.get(key) is None:
|
||||||
|
raise ValueError(f"Missing required key {key}")
|
||||||
|
return func(*[data[key] for key in keys], **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return wrapper_outter
|
||||||
|
|
||||||
|
def _validate_ip(ip: str) -> str:
|
||||||
|
try:
|
||||||
|
netaddr.IPAddress(ip)
|
||||||
|
except netaddr.core.AddrFormatError as e:
|
||||||
|
raise ValueError(f"Invalid IP address {ip}") from e
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def _validate_cidr(cidr: str, family: int) -> str:
|
||||||
|
try:
|
||||||
|
network = netaddr.IPNetwork(cidr)
|
||||||
|
if network.version != family:
|
||||||
|
raise ValueError(f"Invalid CIDR family {network.version}")
|
||||||
|
except netaddr.core.AddrFormatError as e:
|
||||||
|
raise ValueError(f"Invalid CIDR {cidr}") from e
|
||||||
|
return cidr
|
||||||
|
|
||||||
|
def _validate_distribution(distribution: str) -> None:
|
||||||
|
if distribution not in DISTRIBUTIONS:
|
||||||
|
raise ValueError(f"Invalid distribution {distribution}")
|
||||||
|
return distribution
|
||||||
|
|
||||||
|
def _validate_node(node: dict, node_cidr: str, distribution: str) -> None:
|
||||||
|
if not node.get("name"):
|
||||||
|
raise ValueError(f"Node {node.get('name')} is missing a name")
|
||||||
|
if not node.get("username") and distribution not in ["k0s", "k3s"]:
|
||||||
|
raise ValueError(f"Node {node.get('username')} is missing a username")
|
||||||
|
if not node.get("diskSerial") and distribution in ["talos"]:
|
||||||
|
raise ValueError(f"Node {node.get('diskSerial')} is missing a disk serial")
|
||||||
|
ip = _validate_ip(node.get("address"))
|
||||||
|
if netaddr.IPAddress(ip, 4) not in netaddr.IPNetwork(node_cidr):
|
||||||
|
raise ValueError(f"Node {node.get('address')} is not in the node CIDR {node_cidr}")
|
||||||
|
port = 50000 if distribution == "talos" else 22
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.settimeout(5)
|
||||||
|
result = sock.connect_ex((ip, port))
|
||||||
|
if result != 0:
|
||||||
|
raise ValueError(f"Node {ip} port {port} is not open")
|
||||||
|
|
||||||
|
def validate_python_version() -> None:
|
||||||
|
required_version = (3, 11, 0)
|
||||||
|
if sys.version_info < required_version:
|
||||||
|
raise ValueError(f"Python version is below 3.11. Please upgrade.")
|
||||||
|
|
||||||
|
@required("bootstrap_distribution")
|
||||||
|
def validate_cli_tools(distribution: str, **_) -> None:
|
||||||
|
distro = _validate_distribution(distribution)
|
||||||
|
for tool in GLOBAL_CLI_TOOLS:
|
||||||
|
if not which(tool):
|
||||||
|
raise ValueError(f"Missing required CLI tool {tool}")
|
||||||
|
for tool in TALOS_CLI_TOOLS if distro == "talos" else []:
|
||||||
|
if not which(tool):
|
||||||
|
raise ValueError(f"Missing required CLI tool {tool}")
|
||||||
|
for tool in K0S_CLI_TOOLS if distro == "k0s" else []:
|
||||||
|
if not which(tool):
|
||||||
|
raise ValueError(f"Missing required CLI tool {tool}")
|
||||||
|
|
||||||
|
@required("bootstrap_distribution")
|
||||||
|
def validate_distribution(distribution: str, **_) -> None:
|
||||||
|
_validate_distribution(distribution)
|
||||||
|
|
||||||
|
@required("bootstrap_github_username", "bootstrap_github_repository_name", "bootstrap_github_repository_branch")
|
||||||
|
def validate_github(username: str, repository: str, branch: str, **_) -> None:
|
||||||
|
try:
|
||||||
|
request = requests.get(f"https://api.github.com/repos/{username}/{repository}/branches/{branch}")
|
||||||
|
if request.status_code != 200:
|
||||||
|
raise ValueError(f"GitHub repository {username}/{repository} branch {branch} not found")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise ValueError(f"GitHub repository {username}/{repository} branch {branch} not found") from e
|
||||||
|
|
||||||
|
@required("bootstrap_age_public_key")
|
||||||
|
def validate_age(key: str, **_) -> None:
|
||||||
|
if not re.match(r"^age1[a-z0-9]{0,58}$", key):
|
||||||
|
raise ValueError(f"Invalid Age public key {key}")
|
||||||
|
|
||||||
|
@required("bootstrap_timezone")
|
||||||
|
def validate_timezone(timezone: str, **_) -> None:
|
||||||
|
if not timezone in available_timezones():
|
||||||
|
raise ValueError(f"Invalid timezone {timezone}")
|
||||||
|
|
||||||
|
@required("bootstrap_ipv6_enabled", "bootstrap_cluster_cidr", "bootstrap_service_cidr")
|
||||||
|
def validate_cluster_cidrs(ipv6_enabled: bool, cluster_cidr: str, service_cidr: str, **_) -> None:
|
||||||
|
if not isinstance(ipv6_enabled, bool):
|
||||||
|
raise ValueError(f"Invalid IPv6 enabled {ipv6_enabled}")
|
||||||
|
|
||||||
|
if cluster_cidr == service_cidr:
|
||||||
|
raise ValueError(f"Cluster CIDR {cluster_cidr} is the same as service CIDR {service_cidr}")
|
||||||
|
|
||||||
|
if ipv6_enabled:
|
||||||
|
if len(cluster_cidr.split(",")) != 2:
|
||||||
|
raise ValueError(f"Invalid cluster CIDR {cluster_cidr}")
|
||||||
|
if len(service_cidr.split(",")) != 2:
|
||||||
|
raise ValueError(f"Invalid service CIDR {service_cidr}")
|
||||||
|
cluster_ipv4, cluster_ipv6 = cluster_cidr.split(",")
|
||||||
|
_validate_cidr(cluster_ipv4, 4)
|
||||||
|
_validate_cidr(cluster_ipv6, 6)
|
||||||
|
service_ipv4, service_ipv6 = service_cidr.split(",")
|
||||||
|
_validate_cidr(service_ipv4, 4)
|
||||||
|
_validate_cidr(service_ipv6, 6)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(cluster_cidr.split(",")) != 1:
|
||||||
|
raise ValueError(f"Invalid cluster CIDR {cluster_cidr}")
|
||||||
|
if len(service_cidr.split(",")) != 1:
|
||||||
|
raise ValueError(f"Invalid service CIDR {service_cidr}")
|
||||||
|
|
||||||
|
_validate_cidr(cluster_cidr, 4)
|
||||||
|
_validate_cidr(service_cidr, 4)
|
||||||
|
|
||||||
|
@required("bootstrap_acme_email", "bootstrap_acme_production_enabled")
|
||||||
|
def validate_acme_email(email: str, acme_production: bool, **_) -> None:
|
||||||
|
try:
|
||||||
|
validate_email(email)
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
raise ValueError(f"Invalid ACME email {email}") from e
|
||||||
|
|
||||||
|
@required("bootstrap_flux_github_webhook_token")
|
||||||
|
def validate_flux_github_webhook_token(token: str, **_) -> None:
|
||||||
|
if not re.match(r"^[a-zA-Z0-9]+$", token):
|
||||||
|
raise ValueError(f"Invalid Flux GitHub webhook token ***")
|
||||||
|
|
||||||
|
@required("bootstrap_cloudflare_domain", "bootstrap_cloudflare_token", "bootstrap_cloudflare_account_tag", "bootstrap_cloudflare_tunnel_secret", "bootstrap_cloudflare_tunnel_id")
|
||||||
|
def validate_cloudflare(domain: str, token: str, account_tag: str, tunnel_secret: str, tunnel_id: str, **_) -> None:
|
||||||
|
try:
|
||||||
|
cf = CloudFlare.CloudFlare(token=token)
|
||||||
|
zones = cf.zones.get(params={"name": domain})
|
||||||
|
if not zones:
|
||||||
|
raise ValueError(f"Cloudflare domain {domain} not found or token does not have access to it")
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
raise ValueError(f"Cloudflare domain {domain} not found or token does not have access to it") from e
|
||||||
|
|
||||||
|
@required("bootstrap_cilium_loadbalancer_mode")
|
||||||
|
def validate_cilium_loadbalancer_mode(mode: str, **_) -> None:
|
||||||
|
if mode not in CILIUM_LOADBALANCER_MODES:
|
||||||
|
raise ValueError(f"Invalid Cilium load balancer mode {mode}")
|
||||||
|
|
||||||
|
@required("bootstrap_local_storage_path")
|
||||||
|
def validate_local_storage_path(path: str, **_) -> None:
|
||||||
|
try:
|
||||||
|
if not is_valid_filepath(path, platform="linux"):
|
||||||
|
raise ValueError(f"Invalid local storage path {path}")
|
||||||
|
except ValidationError as e:
|
||||||
|
raise ValueError(f"Invalid local storage path {path}") from e
|
||||||
|
|
||||||
|
@required("bootstrap_dns_server")
|
||||||
|
def validate_bootstrap_dns_server(dns_server: str, **_) -> None:
|
||||||
|
_validate_ip(dns_server)
|
||||||
|
|
||||||
|
@required("bootstrap_node_cidr", "bootstrap_kube_api_addr", "bootstrap_k8s_gateway_addr", "bootstrap_external_ingress_addr", "bootstrap_internal_ingress_addr")
|
||||||
|
def validate_host_network(node_cidr: str, api_addr: str, gateway_addr: str, external_ingress_addr: str, internal_ingress_addr: str, **_) -> None:
|
||||||
|
_validate_cidr(node_cidr, 4)
|
||||||
|
_validate_ip(api_addr)
|
||||||
|
_validate_ip(gateway_addr)
|
||||||
|
_validate_ip(external_ingress_addr)
|
||||||
|
_validate_ip(internal_ingress_addr)
|
||||||
|
|
||||||
|
addrs = [api_addr, gateway_addr, external_ingress_addr, internal_ingress_addr]
|
||||||
|
unique = set(addrs)
|
||||||
|
if len(addrs) != len(unique):
|
||||||
|
raise ValueError(f"{api_addr} and {gateway_addr} and {external_ingress_addr} and {internal_ingress_addr} are not unique")
|
||||||
|
|
||||||
|
node_cidr = netaddr.IPNetwork(node_cidr)
|
||||||
|
if netaddr.IPAddress(api_addr) not in node_cidr:
|
||||||
|
raise ValueError(f"Kubernetes API address {api_addr} is not in the node CIDR {node_cidr}")
|
||||||
|
if netaddr.IPAddress(gateway_addr) not in node_cidr:
|
||||||
|
raise ValueError(f"Kubernetes gateway address {gateway_addr} is not in the node CIDR {node_cidr}")
|
||||||
|
if netaddr.IPAddress(external_ingress_addr) not in node_cidr:
|
||||||
|
raise ValueError(f"Kubernetes external ingress address {external_ingress_addr} is not in the node CIDR {node_cidr}")
|
||||||
|
if netaddr.IPAddress(internal_ingress_addr) not in node_cidr:
|
||||||
|
raise ValueError(f"Kubernetes internal ingress address {internal_ingress_addr} is not in the node CIDR {node_cidr}")
|
||||||
|
|
||||||
|
@required("bootstrap_node_cidr", "bootstrap_nodes", "bootstrap_distribution")
|
||||||
|
def validate_nodes(node_cidr: str, nodes: dict[list], distribution: str, **_) -> None:
|
||||||
|
node_cidr = _validate_cidr(node_cidr, 4)
|
||||||
|
|
||||||
|
masters = nodes.get("master", [])
|
||||||
|
if len(masters) < 1:
|
||||||
|
raise ValueError(f"Must have at least one master node")
|
||||||
|
if len(masters) % 2 == 0:
|
||||||
|
raise ValueError(f"Must have an odd number of master nodes")
|
||||||
|
for node in masters:
|
||||||
|
_validate_node(node, node_cidr, distribution)
|
||||||
|
|
||||||
|
workers = nodes.get("worker", [])
|
||||||
|
for node in workers:
|
||||||
|
_validate_node(node, node_cidr, distribution)
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Query age key file
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "{{ repository_path }}/age.key"
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if age key file exists
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.stat.exists
|
|
||||||
success_msg: Age file {{ repository_path }}/age.key exists
|
|
||||||
fail_msg: Age file {{ repository_path }}/age.key does not exist
|
|
||||||
|
|
||||||
- name: Query age key file contents
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
age_contents: "{{ lookup('file', repository_path + '/age.key') }}"
|
|
||||||
|
|
||||||
- name: Check if age public keys match
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_age_public_key in age_contents
|
|
||||||
success_msg: Age public key {{ bootstrap_age_public_key }} exists
|
|
||||||
fail_msg: Age public key {{ bootstrap_age_public_key }} does not exist
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Check if required CLI tools are present
|
|
||||||
ansible.builtin.shell: command -v {{ item }} >/dev/null 2>&1
|
|
||||||
loop: ["age", "cloudflared", "flux", "sops", "jq", "kubeconform", "kustomize"]
|
|
||||||
changed_when: false
|
|
||||||
check_mode: false
|
|
||||||
register: result
|
|
||||||
failed_when: result.rc != 0 and result.rc != 127
|
|
||||||
|
|
||||||
- name: Check if required CLI tools are present for k0s
|
|
||||||
when: bootstrap_distribution == "k0s"
|
|
||||||
ansible.builtin.shell: command -v {{ item }} >/dev/null 2>&1
|
|
||||||
loop: ["k0sctl"]
|
|
||||||
changed_when: false
|
|
||||||
check_mode: false
|
|
||||||
register: result
|
|
||||||
failed_when: result.rc != 0 and result.rc != 127
|
|
||||||
|
|
||||||
- name: Check if required CLI tools are present for talos
|
|
||||||
when: bootstrap_distribution == "talos"
|
|
||||||
ansible.builtin.shell: command -v {{ item }} >/dev/null 2>&1
|
|
||||||
loop: ["talhelper", "talosctl"]
|
|
||||||
changed_when: false
|
|
||||||
check_mode: false
|
|
||||||
register: result
|
|
||||||
failed_when: result.rc != 0 and result.rc != 127
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Query Cloudflare zone
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: https://api.cloudflare.com/client/v4/zones?name={{ bootstrap_cloudflare_domain }}&status=active
|
|
||||||
headers:
|
|
||||||
Authorization: Bearer {{ bootstrap_cloudflare_token }}
|
|
||||||
Content-Type: application/json
|
|
||||||
timeout: 5
|
|
||||||
return_content: true
|
|
||||||
body_format: json
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if Cloudflare zone exists
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.json.success is true
|
|
||||||
success_msg: Cloudflare zone {{ bootstrap_cloudflare_domain }} exists
|
|
||||||
fail_msg: Cloudflare zone {{ bootstrap_cloudflare_domain }} does not exist
|
|
||||||
|
|
||||||
- name: Query Cloudflared tunnel
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: https://api.cloudflare.com/client/v4/accounts/{{ bootstrap_cloudflare_account_tag }}/cfd_tunnel/{{ bootstrap_cloudflare_tunnel_id }}
|
|
||||||
headers:
|
|
||||||
Authorization: Bearer {{ bootstrap_cloudflare_token }}
|
|
||||||
Content-Type: application/json
|
|
||||||
timeout: 5
|
|
||||||
return_content: true
|
|
||||||
body_format: json
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if Cloudflared tunnel exists
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.json.success is true
|
|
||||||
success_msg: Cloudflared tunnel {{ bootstrap_cloudflare_tunnel_id }} exists
|
|
||||||
fail_msg: Cloudflared tunnel {{ bootstrap_cloudflare_tunnel_id }} does not exist
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Query Github username
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: https://api.github.com/users/{{ bootstrap_github_username }}
|
|
||||||
timeout: 5
|
|
||||||
return_content: true
|
|
||||||
body_format: json
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if username exists
|
|
||||||
when: not ci_test|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.json.login == bootstrap_github_username
|
|
||||||
success_msg: Github user {{ bootstrap_github_username }} exists
|
|
||||||
fail_msg: Github user {{ bootstrap_github_username }} does not exist
|
|
||||||
|
|
||||||
- name: Query Github repo
|
|
||||||
when: (not ci_test|default(false)) and (not bootstrap_private_github_repo|default(false))
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: https://api.github.com/repos/{{ bootstrap_github_username }}/{{ bootstrap_github_repository_name }}
|
|
||||||
timeout: 5
|
|
||||||
return_content: true
|
|
||||||
body_format: json
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if repo exists
|
|
||||||
when: (not ci_test|default(false)) and (not bootstrap_private_github_repo|default(false))
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.json.full_name == bootstrap_github_username + '/' + bootstrap_github_repository_name
|
|
||||||
success_msg: Github repo {{ bootstrap_github_username }}/{{ bootstrap_github_repository_name }} exists
|
|
||||||
fail_msg: Github repo {{ bootstrap_github_username }}/{{ bootstrap_github_repository_name }} does not exist
|
|
||||||
|
|
||||||
- name: Query Github repo branch
|
|
||||||
when: (not ci_test|default(false)) and (not bootstrap_private_github_repo|default(false))
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: https://api.github.com/repos/{{ bootstrap_github_username }}/{{ bootstrap_github_repository_name }}/branches/{{ bootstrap_github_repository_branch|default('main', true) }}
|
|
||||||
timeout: 5
|
|
||||||
return_content: true
|
|
||||||
body_format: json
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Check if repo branch exists
|
|
||||||
when: (not ci_test|default(false)) and (not bootstrap_private_github_repo|default(false))
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: result.json.name == bootstrap_github_repository_branch|default('main', true)
|
|
||||||
success_msg: Github repo branch {{ bootstrap_github_repository_branch|default('main', true) }} exists
|
|
||||||
fail_msg: Github repo branch {{ bootstrap_github_repository_branch|default('main', true) }} does not exist
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Verify configuration
|
|
||||||
ansible.builtin.include_tasks: "{{ task }}.yaml"
|
|
||||||
loop: [vars, age, cli, net, cloudflare, github]
|
|
||||||
loop_control:
|
|
||||||
loop_var: task
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Check if master node count is odd
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- bootstrap_nodes.master | length > 0
|
|
||||||
- bootstrap_nodes.master | length is odd
|
|
||||||
success_msg: Master node count {{ bootstrap_nodes.master | length }} is correct.
|
|
||||||
fail_msg: Master node count {{ bootstrap_nodes.master | length }} is not greater than 0 or is not odd.
|
|
||||||
|
|
||||||
- name: Check if node CIDR is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr is ansible.utils.ipv4
|
|
||||||
success_msg: Node CIDR {{ bootstrap_node_cidr }} is valid.
|
|
||||||
fail_msg: Node CIDR {{ bootstrap_node_cidr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if cluster CIDR is ipv4 OR ipv6
|
|
||||||
when: not bootstrap_ipv6_enabled|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_cluster_cidr is ansible.utils.ipv4 or bootstrap_cluster_cidr is ansible.utils.ipv6
|
|
||||||
success_msg: Cluster CIDR {{ bootstrap_cluster_cidr }} is valid.
|
|
||||||
fail_msg: Cluster CIDR {{ bootstrap_cluster_cidr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if service CIDR is ipv4 OR ipv6
|
|
||||||
when: not bootstrap_ipv6_enabled|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_service_cidr is ansible.utils.ipv4 or bootstrap_service_cidr is ansible.utils.ipv6
|
|
||||||
success_msg: Service CIDR {{ bootstrap_service_cidr }} is valid.
|
|
||||||
fail_msg: Service CIDR {{ bootstrap_service_cidr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if cluster CIDR is ipv4 AND ipv6
|
|
||||||
when: bootstrap_ipv6_enabled|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: >
|
|
||||||
(
|
|
||||||
bootstrap_cluster_cidr.split(',')[0] is ansible.utils.ipv4 or
|
|
||||||
bootstrap_cluster_cidr.split(',')[1] is ansible.utils.ipv4
|
|
||||||
) and (
|
|
||||||
bootstrap_cluster_cidr.split(',')[1] is ansible.utils.ipv6 or
|
|
||||||
bootstrap_cluster_cidr.split(',')[0] is ansible.utils.ipv6
|
|
||||||
)
|
|
||||||
success_msg: Cluster CIDR {{ bootstrap_cluster_cidr }} is valid.
|
|
||||||
fail_msg: Cluster CIDR {{ bootstrap_cluster_cidr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if service CIDR is ipv4 AND ipv6
|
|
||||||
when: bootstrap_ipv6_enabled|default(false)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: >
|
|
||||||
(
|
|
||||||
bootstrap_service_cidr.split(',')[0] is ansible.utils.ipv4 or
|
|
||||||
bootstrap_service_cidr.split(',')[1] is ansible.utils.ipv4
|
|
||||||
) and (
|
|
||||||
bootstrap_service_cidr.split(',')[1] is ansible.utils.ipv6 or
|
|
||||||
bootstrap_service_cidr.split(',')[0] is ansible.utils.ipv6
|
|
||||||
)
|
|
||||||
success_msg: Service CIDR {{ bootstrap_service_cidr }} is valid.
|
|
||||||
fail_msg: Service CIDR {{ bootstrap_service_cidr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if k8s_gateway is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_k8s_gateway_addr is ansible.utils.ipv4
|
|
||||||
success_msg: k8s_gateway address {{ bootstrap_k8s_gateway_addr }} is valid.
|
|
||||||
fail_msg: k8s_gateway address {{ bootstrap_k8s_gateway_addr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if k8s_gateway is in node CIDR
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr | ansible.utils.network_in_usable(bootstrap_k8s_gateway_addr)
|
|
||||||
success_msg: k8s_gateway address {{ bootstrap_k8s_gateway_addr }} is within {{ bootstrap_node_cidr }}.
|
|
||||||
fail_msg: k8s_gateway address {{ bootstrap_k8s_gateway_addr }} is not within {{ bootstrap_node_cidr }}.
|
|
||||||
|
|
||||||
- name: Check if internal ingress is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_internal_ingress_addr is ansible.utils.ipv4
|
|
||||||
success_msg: internal ingress address {{ bootstrap_internal_ingress_addr }} is valid.
|
|
||||||
fail_msg: internal ingress address {{ bootstrap_internal_ingress_addr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if internal ingress is in node CIDR
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr | ansible.utils.network_in_usable(bootstrap_internal_ingress_addr)
|
|
||||||
success_msg: internal ingress address {{ bootstrap_internal_ingress_addr }} is within {{ bootstrap_node_cidr }}.
|
|
||||||
fail_msg: internal ingress address {{ bootstrap_internal_ingress_addr }} is not within {{ bootstrap_node_cidr }}.
|
|
||||||
|
|
||||||
- name: Check if external ingress is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_external_ingress_addr is ansible.utils.ipv4
|
|
||||||
success_msg: external ingress address {{ bootstrap_external_ingress_addr }} is valid.
|
|
||||||
fail_msg: external ingress address {{ bootstrap_external_ingress_addr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check external ingress is in node CIDR
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr | ansible.utils.network_in_usable(bootstrap_external_ingress_addr)
|
|
||||||
success_msg: external ingress address {{ bootstrap_external_ingress_addr }} is within {{ bootstrap_node_cidr }}.
|
|
||||||
fail_msg: external ingress address {{ bootstrap_external_ingress_addr }} is not within {{ bootstrap_node_cidr }}.
|
|
||||||
|
|
||||||
- name: Check if Kube API address is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_kube_api_addr is ansible.utils.ipv4
|
|
||||||
success_msg: Kube API address {{ bootstrap_kube_api_addr }} is valid.
|
|
||||||
fail_msg: Kube API address {{ bootstrap_kube_api_addr }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if Kube API address is in node CIDR
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr | ansible.utils.network_in_usable(bootstrap_kube_api_addr)
|
|
||||||
success_msg: Kube API address {{ bootstrap_kube_api_addr }} is within {{ bootstrap_node_cidr }}.
|
|
||||||
fail_msg: Kube API address {{ bootstrap_kube_api_addr }} is not within {{ bootstrap_node_cidr }}.
|
|
||||||
|
|
||||||
- name: Check if DNS server is ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_dns_server is ansible.utils.ipv4
|
|
||||||
success_msg: DNS server {{ bootstrap_dns_server }} is valid.
|
|
||||||
fail_msg: DNS server {{ bootstrap_dns_server }} is invalid.
|
|
||||||
|
|
||||||
- name: Check if all IP addresses are unique
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: >
|
|
||||||
[
|
|
||||||
bootstrap_k8s_gateway_addr,
|
|
||||||
bootstrap_external_ingress_addr,
|
|
||||||
bootstrap_internal_ingress_addr,
|
|
||||||
bootstrap_kube_api_addr
|
|
||||||
] | unique | length == 4
|
|
||||||
success_msg: All IP addresses are unique.
|
|
||||||
fail_msg: All IP addresses are not unique.
|
|
||||||
|
|
||||||
- name: Check if nodes are not the same IPs as k8s_gateway or ingress external/internal
|
|
||||||
when: not bootstrap_kube_vip_enabled|default(true)
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: item.address not in (bootstrap_k8s_gateway_addr, bootstrap_external_ingress_addr, bootstrap_internal_ingress_addr)
|
|
||||||
success_msg: Node address {{ item.address }} is different than k8s_gateway or ingress-nginx.
|
|
||||||
fail_msg: Node address {{ item.address }} is not different than k8s_gateway or ingress-nginx.
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
|
|
||||||
- name: Check if nodes are not the same IPs as k8s_gateway, ingress external/internal or Kube API address
|
|
||||||
when: (bootstrap_distribution == "k3s") and (bootstrap_kube_vip_enabled|default(true))
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: item.address not in (bootstrap_k8s_gateway_addr, bootstrap_external_ingress_addr, bootstrap_internal_ingress_addr, bootstrap_kube_api_addr)
|
|
||||||
success_msg: Node address {{ item.address }} is different than k8s_gateway, ingress-nginx or Kube API.
|
|
||||||
fail_msg: Node address {{ item.address }} is not different than k8s_gateway, ingress-nginx or Kube API.
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
|
|
||||||
- name: Check if node addresses are ipv4
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: item.address is ansible.utils.ipv4
|
|
||||||
success_msg: Node address {{ item.address }} is valid.
|
|
||||||
fail_msg: Node address {{ item.address }} is invalid.
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
|
|
||||||
- name: Check if node addresses are in node CIDR
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_node_cidr | ansible.utils.network_in_usable(item.address)
|
|
||||||
success_msg: Node address {{ item.address }} is within {{ bootstrap_node_cidr }}.
|
|
||||||
fail_msg: Node address {{ item.address }} is not within {{ bootstrap_node_cidr }}.
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
|
|
||||||
- name: Check if node IP addresses are unique
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: >
|
|
||||||
(
|
|
||||||
(bootstrap_nodes.master + bootstrap_nodes.worker|default([])) | map(attribute='address') | list
|
|
||||||
) | unique | length
|
|
||||||
==
|
|
||||||
(
|
|
||||||
(bootstrap_nodes.master + bootstrap_nodes.worker|default([])) | map(attribute='address') | list
|
|
||||||
) | length
|
|
||||||
success_msg: All node IP addresses are unique.
|
|
||||||
fail_msg: All node IP addresses are not unique.
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Check if node names are unique
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: >
|
|
||||||
(
|
|
||||||
(bootstrap_nodes.master + bootstrap_nodes.worker|default([])) | map(attribute='name') | list
|
|
||||||
) | unique | length
|
|
||||||
==
|
|
||||||
(
|
|
||||||
(bootstrap_nodes.master + bootstrap_nodes.worker|default([])) | map(attribute='name') | list
|
|
||||||
) | length
|
|
||||||
success_msg: All node names are unique.
|
|
||||||
fail_msg: All node names are not unique.
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Check if SSH ports are reachable
|
|
||||||
when: (not ci_test|default(false)) and (not bootstrap_distribution == "talos")
|
|
||||||
ansible.builtin.wait_for:
|
|
||||||
host: "{{ item.address }}"
|
|
||||||
port: 22
|
|
||||||
timeout: 10
|
|
||||||
connection: local
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
|
|
||||||
- name: Check if Talos ports are reachable
|
|
||||||
when: (not ci_test|default(false)) and (bootstrap_distribution == "talos")
|
|
||||||
ansible.builtin.wait_for:
|
|
||||||
host: "{{ item.address }}"
|
|
||||||
port: 50000
|
|
||||||
timeout: 10
|
|
||||||
connection: local
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.address }}"
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Check if required bootstrap vars are set
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- item in vars
|
|
||||||
- vars[item] != None
|
|
||||||
success_msg: Required bootstrap var {{ item }} exists and is defined
|
|
||||||
fail_msg: Required bootstrap var {{ item }} does not exists or is not defined
|
|
||||||
loop:
|
|
||||||
- bootstrap_acme_email
|
|
||||||
- bootstrap_age_public_key
|
|
||||||
- bootstrap_cilium_loadbalancer_mode
|
|
||||||
- bootstrap_cloudflare_account_tag
|
|
||||||
- bootstrap_cloudflare_domain
|
|
||||||
- bootstrap_cloudflare_token
|
|
||||||
- bootstrap_cloudflare_tunnel_id
|
|
||||||
- bootstrap_cloudflare_tunnel_secret
|
|
||||||
- bootstrap_cluster_cidr
|
|
||||||
- bootstrap_distribution
|
|
||||||
- bootstrap_dns_server
|
|
||||||
- bootstrap_external_ingress_addr
|
|
||||||
- bootstrap_flux_github_webhook_token
|
|
||||||
- bootstrap_github_repository_branch
|
|
||||||
- bootstrap_github_repository_name
|
|
||||||
- bootstrap_github_username
|
|
||||||
- bootstrap_internal_ingress_addr
|
|
||||||
- bootstrap_ipv6_enabled
|
|
||||||
- bootstrap_k8s_gateway_addr
|
|
||||||
- bootstrap_kube_api_addr
|
|
||||||
- bootstrap_local_storage_path
|
|
||||||
- bootstrap_node_cidr
|
|
||||||
- bootstrap_service_cidr
|
|
||||||
- bootstrap_timezone
|
|
||||||
|
|
||||||
- name: Check if bootstrap distribution is valid
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: bootstrap_distribution in ['k0s', 'k3s', 'talos']
|
|
||||||
success_msg: Distribution {{ bootstrap_distribution }} is valid
|
|
||||||
fail_msg: Distribution {{ bootstrap_distribution }} is not valid
|
|
||||||
|
|
||||||
- name: Check if bootstrap node names are valid
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that: item.name is match('^[a-z0-9-\.]+$')
|
|
||||||
success_msg: Node name {{ item.name }} is valid
|
|
||||||
fail_msg: Node name {{ item.name }} is not valid
|
|
||||||
loop: "{{ bootstrap_nodes.master + bootstrap_nodes.worker|default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.name }}"
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#% if bootstrap_distribution in ['k3s', 'k0s'] %#
|
||||||
|
# Ansible
|
||||||
|
ansible-lint==6.22.2
|
||||||
|
ansible==9.1.0
|
||||||
|
jmespath==1.0.1
|
||||||
|
openshift==0.13.2
|
||||||
|
#% endif %#
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#% if bootstrap_distribution in ['k3s', 'k0s'] %#
|
||||||
---
|
---
|
||||||
collections:
|
collections:
|
||||||
- name: ansible.posix
|
- name: ansible.posix
|
||||||
@@ -8,7 +9,10 @@ collections:
|
|||||||
version: 8.2.0
|
version: 8.2.0
|
||||||
- name: kubernetes.core
|
- name: kubernetes.core
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
|
#% if bootstrap_distribution == 'k3s' %#
|
||||||
roles:
|
roles:
|
||||||
- name: xanmanning.k3s
|
- name: xanmanning.k3s
|
||||||
src: https://github.com/PyratLabs/ansible-role-k3s
|
src: https://github.com/PyratLabs/ansible-role-k3s
|
||||||
version: v3.4.3
|
version: v3.4.3
|
||||||
|
#% endif %#
|
||||||
|
#% endif %#
|
||||||
@@ -8,6 +8,6 @@ resources:
|
|||||||
- ./kubelet-csr-approver/ks.yaml
|
- ./kubelet-csr-approver/ks.yaml
|
||||||
#% endif %#
|
#% endif %#
|
||||||
- ./metrics-server/ks.yaml
|
- ./metrics-server/ks.yaml
|
||||||
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel|default({}) and spegel.enabled|default(false) %#
|
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel.enabled %#
|
||||||
- ./spegel/ks.yaml
|
- ./spegel/ks.yaml
|
||||||
#% endif %#
|
#% endif %#
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel|default({}) and spegel.enabled|default(false) %#
|
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel.enabled %#
|
||||||
---
|
---
|
||||||
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||||
kind: HelmRelease
|
kind: HelmRelease
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel|default({}) and spegel.enabled|default(false) %#
|
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel.enabled %#
|
||||||
---
|
---
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel|default({}) and spegel.enabled|default(false) %#
|
#% if bootstrap_distribution in ['k0s', 'talos'] and spegel.enabled %#
|
||||||
---
|
---
|
||||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ spec:
|
|||||||
before:
|
before:
|
||||||
- sudo bash /home/#{ item.username }#/k0s/hooks/apply-system.sh
|
- sudo bash /home/#{ item.username }#/k0s/hooks/apply-system.sh
|
||||||
- sudo mv /home/#{ item.username }#/k0s/containerd/unprivileged-ports.toml /etc/k0s/containerd.d/unprivileged-ports.toml
|
- sudo mv /home/#{ item.username }#/k0s/containerd/unprivileged-ports.toml /etc/k0s/containerd.d/unprivileged-ports.toml
|
||||||
#% if spegel|default({}) and spegel.enabled|default(false) %#
|
#% if spegel.enabled %#
|
||||||
- sudo mv /home/#{ item.username }#/k0s/containerd/spegel.toml /etc/k0s/containerd.d/spegel.toml
|
- sudo mv /home/#{ item.username }#/k0s/containerd/spegel.toml /etc/k0s/containerd.d/spegel.toml
|
||||||
#% endif %#
|
#% endif %#
|
||||||
reset:
|
reset:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#% if bootstrap_distribution == 'k0s' and spegel|default({}) and spegel.enabled|default(false) %#
|
#% if bootstrap_distribution == 'k0s' and spegel.enabled %#
|
||||||
[plugins."io.containerd.grpc.v1.cri".registry]
|
[plugins."io.containerd.grpc.v1.cri".registry]
|
||||||
config_path = "/var/lib/k0s/containerd/certs.d"
|
config_path = "/var/lib/k0s/containerd/certs.d"
|
||||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ controlPlane:
|
|||||||
[plugins."io.containerd.grpc.v1.cri"]
|
[plugins."io.containerd.grpc.v1.cri"]
|
||||||
enable_unprivileged_ports = true
|
enable_unprivileged_ports = true
|
||||||
enable_unprivileged_icmp = true
|
enable_unprivileged_icmp = true
|
||||||
#% if spegel|default({}) and spegel.enabled|default(false) %#
|
#% if spegel.enabled %#
|
||||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||||
discard_unpacked_layers = false
|
discard_unpacked_layers = false
|
||||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
|
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Cluster Installation
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
gather_facts: false
|
|
||||||
vars_files:
|
|
||||||
- vars/config.yaml
|
|
||||||
- vars/addons.yaml
|
|
||||||
tasks:
|
|
||||||
- name: Get absolute path to this Git repository # noqa: command-instead-of-module
|
|
||||||
ansible.builtin.command: git rev-parse --show-toplevel
|
|
||||||
changed_when: false
|
|
||||||
check_mode: false
|
|
||||||
register: repository
|
|
||||||
failed_when: repository.rc != 0
|
|
||||||
|
|
||||||
- name: Set facts
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
repository_path: "{{ repository.stdout }}"
|
|
||||||
|
|
||||||
- name: Override Kube API address when there is a single master node and no address is defined
|
|
||||||
when: bootstrap_nodes.master | length == 1 and not bootstrap_kube_api_addr
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
bootstrap_kube_vip_enabled: false
|
|
||||||
bootstrap_kube_api_addr: "{{ bootstrap_nodes.master[0].address }}"
|
|
||||||
|
|
||||||
- name: Verify configuration
|
|
||||||
ansible.builtin.include_tasks: tasks/validation/main.yaml
|
|
||||||
@@ -7,6 +7,7 @@ import_paths = ["./bootstrap/scripts"]
|
|||||||
loaders = ["loader:Loader"]
|
loaders = ["loader:Loader"]
|
||||||
jinja_suffix = ".j2"
|
jinja_suffix = ".j2"
|
||||||
force = true
|
force = true
|
||||||
|
undefined = "chainable"
|
||||||
|
|
||||||
# Block and comment delimiters are changed to avoid conflicts with Renovate
|
# Block and comment delimiters are changed to avoid conflicts with Renovate
|
||||||
# Variable delimiters are changed to avoid conflicts with Renovate and Go templates
|
# Variable delimiters are changed to avoid conflicts with Renovate and Go templates
|
||||||
|
|||||||
+4
-5
@@ -1,8 +1,7 @@
|
|||||||
ansible-lint==6.22.2
|
bcrypt==4.1.2
|
||||||
ansible==9.1.0
|
cloudflare==2.16.0
|
||||||
bcrypt==4.1.2 # https://github.com/pyca/bcrypt/issues/684
|
email-validator==2.1.0
|
||||||
jmespath==1.0.1
|
|
||||||
makejinja==2.3.5
|
makejinja==2.3.5
|
||||||
netaddr==0.10.1
|
netaddr==0.10.1
|
||||||
openshift==0.13.2
|
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
|
pathvalidate==3.2.0
|
||||||
Reference in new issue
Block a user