From dff2279a8fc5424f320bd284fa67a07d85c599f1 Mon Sep 17 00:00:00 2001 From: Snarf Date: Mon, 2 Nov 2026 17:13:58 -0500 Subject: [PATCH] chore: initialize git repository and add ArgoCD application manifests --- homelab-gitops-repo | 1 + scripts/check_argocd_status.py | 21 +++++ scripts/configure_argocd_notifications.py | 69 ++++++++++++++++ scripts/register_aerodrome_argocd.py | 97 +++++++++++++++++++++++ scripts/setup_gitops_repo.py | 41 ++++++++++ 5 files changed, 229 insertions(+) create mode 160000 homelab-gitops-repo create mode 100644 scripts/check_argocd_status.py create mode 100644 scripts/configure_argocd_notifications.py create mode 100644 scripts/register_aerodrome_argocd.py create mode 100644 scripts/setup_gitops_repo.py diff --git a/homelab-gitops-repo b/homelab-gitops-repo new file mode 160000 index 0000000..cb8e60b --- /dev/null +++ b/homelab-gitops-repo @@ -0,0 +1 @@ +Subproject commit cb8e60b086646e225edc3bd85dc6868033fb6ab1 diff --git a/scripts/check_argocd_status.py b/scripts/check_argocd_status.py new file mode 100644 index 0000000..4b978c0 --- /dev/null +++ b/scripts/check_argocd_status.py @@ -0,0 +1,21 @@ +import paramiko + +def main(): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) + + cmd = "kubectl get pod -n infrastructure -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].spec.containers[*].name}'" + stdin, stdout, stderr = ssh.exec_command(cmd) + container_name = stdout.read().decode().strip() + print("Container name:", container_name) + + # Check cluster list via argocd CLI inside the server pod + # In argocd pod, the binary 'argocd' is usually available or we can check via curl localhost:8080/api/v1/clusters + stdin, stdout, stderr = ssh.exec_command(f"kubectl exec -n infrastructure deployment/argocd-server -c {container_name} -- curl -s http://localhost:8080/api/v1/clusters") + print("API clusters response:", stdout.read().decode()) + + ssh.close() + +if __name__ == '__main__': + main() diff --git a/scripts/configure_argocd_notifications.py b/scripts/configure_argocd_notifications.py new file mode 100644 index 0000000..4f2f622 --- /dev/null +++ b/scripts/configure_argocd_notifications.py @@ -0,0 +1,69 @@ +import paramiko +import yaml + +def main(): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) + + cm_manifest = """ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm + namespace: infrastructure + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/instance: argocd + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd +data: + context: | + argocdUrl: https://argo.snarfnet.net + service.webhook.gotify: | + url: http://140.44.4.70:8090/message?token=A8aWxGiToPsAuToMaTiO + headers: + - name: Content-Type + value: application/json + template.app-sync-status: | + webhook: + gotify: + method: POST + body: | + { + "title": "ArgoCD: {{.app.metadata.name}} ({{.app.status.sync.status}})", + "message": "Application: {{.app.metadata.name}}\\nCluster: {{.app.spec.destination.server}}\\nSync Status: {{.app.status.sync.status}}\\nHealth: {{.app.status.health.status}}\\nURL: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "priority": 4 + } + template.app-health-degraded: | + webhook: + gotify: + method: POST + body: | + { + "title": "ArgoCD: {{.app.metadata.name}} Health DEGRADED", + "message": "Application: {{.app.metadata.name}}\\nCluster: {{.app.spec.destination.server}}\\nHealth: {{.app.status.health.status}}\\nURL: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "priority": 8 + } + trigger.on-sync-failed: | + - description: Application syncing has failed + send: [app-sync-status] + when: app.status.operationState != nil and app.status.operationState.phase in ['Error', 'Failed'] + trigger.on-health-degraded: | + - description: Application health is degraded + send: [app-health-degraded] + when: app.status.health.status == 'Degraded' +""" + stdin, stdout, stderr = ssh.exec_command("kubectl apply -f -") + stdin.write(cm_manifest) + stdin.channel.shutdown_write() + print("CM Apply stdout:\n", stdout.read().decode()) + print("CM Apply stderr:\n", stderr.read().decode()) + + stdin, stdout, stderr = ssh.exec_command("kubectl rollout restart deployment/argocd-notifications-controller -n infrastructure") + print("Rollout restart:\n", stdout.read().decode()) + + ssh.close() + +if __name__ == '__main__': + main() diff --git a/scripts/register_aerodrome_argocd.py b/scripts/register_aerodrome_argocd.py new file mode 100644 index 0000000..6ebba34 --- /dev/null +++ b/scripts/register_aerodrome_argocd.py @@ -0,0 +1,97 @@ +import paramiko +import json +import base64 + +def main(): + # 1. Connect to Aerodrome (140.44.4.11) + ssh_aero = paramiko.SSHClient() + ssh_aero.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_aero.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) + + print("=== STEP 1: Creating argocd-manager ServiceAccount & RBAC on Aerodrome ===") + rb_yaml = """ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: argocd-manager + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: argocd-manager-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: argocd-manager + namespace: kube-system +--- +apiVersion: v1 +kind: Secret +metadata: + name: argocd-manager-token + namespace: kube-system + annotations: + kubernetes.io/service-account.name: argocd-manager +type: kubernetes.io/service-account-token +""" + stdin, stdout, stderr = ssh_aero.exec_command("kubectl apply -f -") + stdin.write(rb_yaml) + stdin.channel.shutdown_write() + print("RBAC apply stdout:\n", stdout.read().decode()) + print("RBAC apply stderr:\n", stderr.read().decode()) + + # Get CA cert and Token + stdin, stdout, stderr = ssh_aero.exec_command("kubectl get secret -n kube-system argocd-manager-token -o json") + secret_data = json.loads(stdout.read().decode()) + token = base64.b64decode(secret_data["data"]["token"]).decode('utf-8') + ca_data = secret_data["data"]["ca.crt"] + print(f"Obtained Aerodrome token (len: {len(token)}) and CA cert.") + ssh_aero.close() + + # 2. Connect to Spaceport (140.44.4.41) and register cluster in ArgoCD + ssh_sp = paramiko.SSHClient() + ssh_sp.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_sp.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) + + print("=== STEP 2: Creating cluster secret in Spaceport (namespace infrastructure) ===") + config_json = json.dumps({ + "bearerToken": token, + "tlsClientConfig": { + "insecure": False, + "caData": ca_data + } + }) + + cluster_secret_yaml = f""" +apiVersion: v1 +kind: Secret +metadata: + name: cluster-aerodrome + namespace: infrastructure + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: aerodrome + server: https://140.44.4.11:6443 + config: | + {config_json} +""" + stdin, stdout, stderr = ssh_sp.exec_command("kubectl apply -f -") + stdin.write(cluster_secret_yaml) + stdin.channel.shutdown_write() + print("Cluster Secret apply stdout:\n", stdout.read().decode()) + print("Cluster Secret apply stderr:\n", stderr.read().decode()) + + # Verify cluster list in argocd + stdin, stdout, stderr = ssh_sp.exec_command("kubectl get secrets -n infrastructure -l argocd.argoproj.io/secret-type=cluster") + print("Cluster Secrets:\n", stdout.read().decode()) + + ssh_sp.close() + +if __name__ == '__main__': + main() diff --git a/scripts/setup_gitops_repo.py b/scripts/setup_gitops_repo.py new file mode 100644 index 0000000..b2cd5c7 --- /dev/null +++ b/scripts/setup_gitops_repo.py @@ -0,0 +1,41 @@ +import paramiko +import json +import sys + +def main(): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) + + # Check if repo already exists + stdin, stdout, stderr = ssh.exec_command('kubectl exec -n infrastructure deployment/gitea -c gitea -- ls -la /data/git/gitea-repositories/snarf') + snarf_repos = stdout.read().decode() + if 'homelab-gitops.git' in snarf_repos: + print("homelab-gitops.git already exists on Gitea!") + else: + print("Generating Gitea API token for Snarf...") + stdin, stdout, stderr = ssh.exec_command("kubectl exec -n infrastructure deployment/gitea -c gitea -- gitea admin user generate-access-token -u Snarf -t gitops-token --scopes all --raw") + token = stdout.read().decode().strip() + print(f"Token obtained (len: {len(token)})") + + create_payload = json.dumps({ + "name": "homelab-gitops", + "description": "Snarfnet Homelab GitOps, Terraform, and ArgoCD Declarations", + "private": False, + "auto_init": True + }) + + # Call Gitea API inside gitea container + curl_cmd = f"kubectl exec -i -n infrastructure deployment/gitea -c gitea -- curl -s -X POST http://localhost:3000/api/v1/user/repos -H \"Authorization: token {token}\" -H \"Content-Type: application/json\" -d @-" + stdin, stdout, stderr = ssh.exec_command(curl_cmd) + stdin.write(create_payload) + stdin.channel.shutdown_write() + resp = stdout.read().decode() + print("API Response:", resp) + + stdin, stdout, stderr = ssh.exec_command('kubectl exec -n infrastructure deployment/gitea -c gitea -- ls -la /data/git/gitea-repositories/snarf') + print("Repos under snarf:\n", stdout.read().decode()) + ssh.close() + +if __name__ == '__main__': + main()