diff --git a/.envrc.example b/.envrc.example index d9b1196..b10066d 100644 --- a/.envrc.example +++ b/.envrc.example @@ -1,10 +1,9 @@ -export VAGRANT_BRIDGE='Intel(R) Ethernet Connection (16) I219-V' - -# Network configuration for Vagrant/Ansible - +# export VAGRANT_BRIDGE='Intel(R) Ethernet Connection (16) I219-V' export WORKSTATION_IP="192.168.56.10" export VM1_IP="192.168.56.80" export VM2_IP="192.168.56.81" export VM3_IP="192.168.56.82" export VAGRANT_NETWORK_PREFIX="192.168.56" export K3S_URL_IP="192.168.56.250" +export METALLB_IP_RANGE="192.168.56.230-192.168.56.240" + diff --git a/.gitignore b/.gitignore index 8a9e3d1..34f8004 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ scripts/ansible_inventory.ini scripts/ansible_inventory.ini vagrant/dev/ubuntu/ansible/ansible_inventory.ini *.cast +vagrant/dev/ubuntu/certs/ diff --git a/pipelines/dev/vagrant-k3s.json b/pipelines/dev/vagrant-k3s.json index 99a86b1..3881c11 100644 --- a/pipelines/dev/vagrant-k3s.json +++ b/pipelines/dev/vagrant-k3s.json @@ -1,5 +1,13 @@ [ - + { + "name": "Checks for .envrc", + "function": "RunCommand", + "params": [ + "./scripts/envrc_checks.sh" + ], + "retryCount": 0, + "shouldAbort": true + }, { "name": "Create Vagrant nodes", "function": "RunCommand", @@ -9,7 +17,6 @@ "retryCount": 0, "shouldAbort": true }, - { "name": "Configure Vagrant K3s", "function": "RunCommand", @@ -19,8 +26,6 @@ "retryCount": 0, "shouldAbort": true }, - - { "name": "Create Vagrant workstation", "function": "RunCommand", @@ -30,4 +35,4 @@ "retryCount": 0, "shouldAbort": true } -] +] \ No newline at end of file diff --git a/scripts/envrc_checks.sh b/scripts/envrc_checks.sh new file mode 100755 index 0000000..f0029d3 --- /dev/null +++ b/scripts/envrc_checks.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# check if an .envrc file exists +if [ ! -f .envrc ]; then + echo ".envrc file not found" + cp .envrc.example .envrc + if [ $? -eq 0 ]; then + echo ".envrc file created from .envrc.example" + else + echo "Failed to create .envrc file" + exit 1 + fi +else + echo ".envrc file found" + cp .envrc vagrant/dev/ubuntu/.envrc + if [ $? -eq 0 ]; then + echo ".envrc file synced to vagrant/dev/ubuntu/.envrc" + else + echo "Failed to sync .envrc file" + exit 1 + fi +fi diff --git a/scripts/install_traefik.sh b/scripts/install_traefik.sh deleted file mode 100755 index d021b66..0000000 --- a/scripts/install_traefik.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash - -if kubectl -n kube-system get pods --no-headers 2>/dev/null | grep -q 'traefik'; then - echo "Traefik is already running in the 'kube-system' namespace. Upgrading instead." - - # Create a temporary values file for more complex configuration - cat > /tmp/traefik-values.yaml < /tmp/traefik-values.yaml < \ No newline at end of file diff --git a/vagrant/dev/ubuntu/ansible/install_dnsmasq.yaml b/vagrant/dev/ubuntu/ansible/install_dnsmasq.yaml new file mode 100644 index 0000000..d74d049 --- /dev/null +++ b/vagrant/dev/ubuntu/ansible/install_dnsmasq.yaml @@ -0,0 +1,78 @@ +--- +- name: Install Dnsmasq on workstation + hosts: localhost + become: true + become_user: root + serial: 1 # Ensure tasks are executed one host at a time + vars_files: + - vars.yaml + + tasks: + + - name: Install dnsmasq + ansible.builtin.apt: + name: dnsmasq + state: present + + + - name: Stop systemd-resolved + ansible.builtin.systemd: + name: systemd-resolved + state: stopped + + - name: Disable systemd-resolved + ansible.builtin.systemd: + name: systemd-resolved + enabled: false + + - name: check to see if /etc/resolv.conf is a symlink + ansible.builtin.stat: + path: /etc/resolv.conf + register: resolv_conf + + - name: Remove /etc/resolv.conf if it is a symlink + ansible.builtin.file: + path: /etc/resolv.conf + state: absent + when: resolv_conf.stat.islnk + + - name: Ensure /etc/resolv.conf is a regular file + ansible.builtin.file: + path: /etc/resolv.conf + state: touch + + - name: Ensure /etc/resolv.conf uses 127.0.0.1 for server + ansible.builtin.lineinfile: + path: /etc/resolv.conf + regexp: '^nameserver' + line: 'nameserver 127.0.0.1' + state: present + + - name: Configure dnsmasq + ansible.builtin.copy: + dest: /etc/dnsmasq.d/k3s-cluster.conf + content: | + address=/{{ dnsmasq_k3s_domain }} + server=1.1.1.1 + server=8.8.8.8 + owner: root + group: root + mode: "0644" + notify: Restart dnsmasq + + - name: Ensure conf-dir is uncommented in /etc/dnsmasq.conf + ansible.builtin.lineinfile: + path: /etc/dnsmasq.conf + regexp: '^#?conf-dir=/etc/dnsmasq.d' + line: 'conf-dir=/etc/dnsmasq.d' + state: present + owner: root + group: root + mode: '0644' + + handlers: + - name: Restart dnsmasq + ansible.builtin.systemd: + name: dnsmasq + state: restarted + diff --git a/vagrant/dev/ubuntu/ansible/provision_workstation.sh b/vagrant/dev/ubuntu/ansible/provision_workstation.sh index 0888e78..ac31459 100644 --- a/vagrant/dev/ubuntu/ansible/provision_workstation.sh +++ b/vagrant/dev/ubuntu/ansible/provision_workstation.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash + sudo apt-get update -sudo apt-get install -y software-properties-common git vim python3.10-venv jq +sudo apt-get install -y software-properties-common git vim python3.10-venv jq figlet + +source /vagrant/.envrc # Set up ansible environment for vagrant user sudo -u vagrant mkdir -p /home/vagrant/.ansible @@ -103,6 +106,7 @@ if ! grep -qF "$BLOCK_START" "$BASHRC"; then eval `ssh-agent -s` ssh-add ~/machines/*/virtualbox/private_key ssh-add -L +source ~/vagrant/.envrc EOF else echo "Provisioning block already present in $BASHRC" @@ -144,7 +148,13 @@ if [ $? -ne 0 ]; then fi # copy_k8s_config.yaml -ANSIBLE_SUPPRESS_INTERPRETER_DISCOVERY_WARNING=1ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook copy_k8s_config.yaml --inventory-file ansible_inventory.ini +ANSIBLE_SUPPRESS_INTERPRETER_DISCOVERY_WARNING=1 ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook copy_k8s_config.yaml --inventory-file ansible_inventory.ini +if [ $? -ne 0 ]; then + echo "Ansible playbook failed. Please check your Vagrant VMs and network configuration." + exit 1 +fi + +ANSIBLE_SUPPRESS_INTERPRETER_DISCOVERY_WARNING=1 ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook install_dnsmasq.yaml --inventory-file ansible_inventory.ini if [ $? -ne 0 ]; then echo "Ansible playbook failed. Please check your Vagrant VMs and network configuration." exit 1 @@ -158,3 +168,5 @@ if [ $? -ne 0 ]; then exit 1 fi + + diff --git a/vagrant/dev/ubuntu/ansible/vars.yaml b/vagrant/dev/ubuntu/ansible/vars.yaml index 9a02a8e..5b1482c 100644 --- a/vagrant/dev/ubuntu/ansible/vars.yaml +++ b/vagrant/dev/ubuntu/ansible/vars.yaml @@ -7,6 +7,8 @@ k3s_url_ip: "{{ lookup('env', 'K3S_URL_IP') | default('192.168.56.250', true) }} workstation_ip: "{{ lookup('env', 'WORKSTATION_IP') | default('192.168.56.10', true) }}" network_prefix: "{{ lookup('env', 'VAGRANT_NETWORK_PREFIX') | default('192.168.56', true) }}" +dnsmasq_k3s_domain: "{{ lookup('env', 'DNSMASQ_K3S_DOMAIN') | default('headshed.it/192.168.56.230', true) }}" + # K3s configuration k3s_cluster_name: "dev-cluster" k3s_token_file: "/opt/k3s-token" diff --git a/vagrant/dev/ubuntu/k8s/nginx-test/deployment.yaml b/vagrant/dev/ubuntu/k8s/nginx-test/deployment.yaml index 63ec4fe..251c952 100644 --- a/vagrant/dev/ubuntu/k8s/nginx-test/deployment.yaml +++ b/vagrant/dev/ubuntu/k8s/nginx-test/deployment.yaml @@ -13,6 +13,14 @@ spec: labels: app: nginx-storage spec: + + initContainers: + - name: init-nginx-content + image: busybox + command: ["sh", "-c", "echo '

Welcome to nginx!

using MVK

https://mvk.headshed.dev/

' > /usr/share/nginx/html/index.html"] + volumeMounts: + - name: nginx-data + mountPath: /usr/share/nginx/html containers: - name: nginx image: nginx:stable diff --git a/vagrant/dev/ubuntu/k8s/nginx-test/ingress.yaml b/vagrant/dev/ubuntu/k8s/nginx-test/ingress.yaml new file mode 100644 index 0000000..e0a7d35 --- /dev/null +++ b/vagrant/dev/ubuntu/k8s/nginx-test/ingress.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: traefik-ingress + namespace: default + # This annotation is good practice to ensure it uses the right entrypoint + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + # This block is the key. It tells Ingress controllers like Traefik + # to use the specified secret for TLS termination for the listed hosts. + tls: + - hosts: + - "*.headshed.it" # Or a specific subdomain like test.headshed.it + secretName: wildcard-headshed-it-tls # <-- The name of the secret you created + + rules: + - host: nginx.headshed.it # The actual domain you will use to access the service + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx-storage # The name of the k8s service for your app + port: + number: 80 # The port your service is listening on diff --git a/vagrant/dev/ubuntu/k8s/nginx-test/service.yaml b/vagrant/dev/ubuntu/k8s/nginx-test/service.yaml new file mode 100644 index 0000000..6ae6ac8 --- /dev/null +++ b/vagrant/dev/ubuntu/k8s/nginx-test/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: nginx-storage + namespace: default +spec: + selector: + app: nginx-storage + ports: + - protocol: TCP + port: 80 + targetPort: 80 diff --git a/vagrant/dev/ubuntu/k8s/traefik-tlsstore.yaml b/vagrant/dev/ubuntu/k8s/traefik-tlsstore.yaml new file mode 100644 index 0000000..146c038 --- /dev/null +++ b/vagrant/dev/ubuntu/k8s/traefik-tlsstore.yaml @@ -0,0 +1,8 @@ +apiVersion: traefik.io/v1alpha1 +kind: TLSStore +metadata: + name: default + namespace: traefik +spec: + defaultCertificate: + secretName: wildcard-headshed-it-tls diff --git a/vagrant/dev/ubuntu/pipelines/vagrant-ingress.json b/vagrant/dev/ubuntu/pipelines/vagrant-ingress.json new file mode 100644 index 0000000..78e6720 --- /dev/null +++ b/vagrant/dev/ubuntu/pipelines/vagrant-ingress.json @@ -0,0 +1,29 @@ +[ + { + "name": "Install Helm", + "function": "RunCommand", + "params": [ + "./scripts/helm_check_install.sh" + ], + "retryCount": 0, + "shouldAbort": true + }, + { + "name": "Install traefik", + "function": "RunCommand", + "params": [ + "./scripts/install_traefik.sh" + ], + "retryCount": 0, + "shouldAbort": true + }, + { + "name": "Wait for Longhorn pods to come up", + "function": "RunCommand", + "params": [ + "./scripts/wait_for_longhorn.sh" + ], + "retryCount": 10, + "shouldAbort": true + } +] \ No newline at end of file diff --git a/vagrant/dev/ubuntu/pipelines/vagrant-metallb.json b/vagrant/dev/ubuntu/pipelines/vagrant-metallb.json new file mode 100644 index 0000000..b859f72 --- /dev/null +++ b/vagrant/dev/ubuntu/pipelines/vagrant-metallb.json @@ -0,0 +1,11 @@ +[ + { + "name": "Install metallb", + "function": "RunCommand", + "params": [ + "./scripts/install_metallb.sh" + ], + "retryCount": 0, + "shouldAbort": true + } +] \ No newline at end of file diff --git a/vagrant/dev/ubuntu/scripts/helm_check_install.sh b/vagrant/dev/ubuntu/scripts/helm_check_install.sh new file mode 100755 index 0000000..947e376 --- /dev/null +++ b/vagrant/dev/ubuntu/scripts/helm_check_install.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# check to see if helm is installed +if ! command -v helm &> /dev/null; then + echo "Helm is not installed. Installing it now ..." + # curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 + + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + if [ $? -ne 0 ]; then + echo "Failed to install Helm." + exit 1 + fi +fi + +helm version diff --git a/vagrant/dev/ubuntu/scripts/install_metallb.sh b/vagrant/dev/ubuntu/scripts/install_metallb.sh new file mode 100755 index 0000000..838d23a --- /dev/null +++ b/vagrant/dev/ubuntu/scripts/install_metallb.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + + +source /vagrant/.envrc + +# Check if MetalLB is already installed by looking for the controller deployment +if ! kubectl get deployment -n metallb-system controller &>/dev/null; then + echo "Installing MetalLB..." + kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/main/config/manifests/metallb-native.yaml + if [ $? -ne 0 ]; then + echo "Fatal: Failed to apply MetalLB manifest." >&2 + exit 1 + fi + + # Wait for MetalLB components to be ready + echo "Waiting for MetalLB components to be ready..." + kubectl wait --namespace metallb-system \ + --for=condition=ready pod \ + --selector=app=metallb \ + --timeout=90s + +else + echo "MetalLB is already installed." +fi + +# Wait for the webhook service to be ready +echo "Waiting for MetalLB webhook service to be ready..." +kubectl wait --namespace metallb-system \ + --for=condition=ready pod \ + --selector=component=webhook \ + --timeout=90s + +# Check if the IPAddressPool already exists +if ! kubectl get ipaddresspool -n metallb-system default &>/dev/null; then + echo "Creating MetalLB IPAddressPool..." + cat </dev/null; then + echo "Creating MetalLB L2Advertisement..." + cat < "$TMPFILE" < /dev/null; then + echo "Traefik is already installed in the 'traefik' namespace. Upgrading..." + helm upgrade traefik traefik/traefik --namespace traefik -f "$TMPFILE" +else + echo "Installing Traefik..." + helm repo add traefik https://traefik.github.io/charts + helm repo update + # Using --create-namespace is good practice, though traefik will always exist. + helm install traefik traefik/traefik --namespace traefik --create-namespace -f "$TMPFILE" +fi + +# Apply the TLS store configuration +kubectl apply -f k8s/traefik-tlsstore.yaml +if [ $? -ne 0 ]; then + echo "Failed to apply TLS store configuration." + exit 1 +fi + + +echo +echo "To access the dashboard:" +echo "kubectl port-forward -n traefik \$(kubectl get pods -n traefik -l \"app.kubernetes.io/name=traefik\" -o name) 9000:9000" +echo "Then visit http://localhost:9000/dashboard/ in your browser" +