|
| 1 | +# VDI Idempotency Implementation Plan |
| 2 | + |
| 3 | +## Overview |
| 4 | +This document outlines the implementation of idempotency for the VDI setup module, ensuring that roles can be safely re-run without causing conflicts or duplicate operations. |
| 5 | + |
| 6 | +## Key Features |
| 7 | + |
| 8 | +### 1. Lock File System |
| 9 | +- **Location**: `/opt/vdi-setup/.vdi-lock.yaml` |
| 10 | +- **Purpose**: Tracks deployment status, role completion, and configuration changes |
| 11 | +- **Structure**: YAML format with deployment metadata and role status |
| 12 | + |
| 13 | +### 2. Role Execution Control |
| 14 | +- **Pattern**: Each role checks if it should run before executing tasks |
| 15 | +- **Logic**: Roles run if: |
| 16 | + - Lock file doesn't exist (fresh deployment) |
| 17 | + - Role is not marked as completed |
| 18 | + - Deployment configuration has changed |
| 19 | + - Force rerun is enabled |
| 20 | + |
| 21 | +### 3. Setup Status Tracking |
| 22 | +- **Status Values**: `configuring`, `available`, `error` |
| 23 | +- **Purpose**: Indicates current VDI setup state for external monitoring |
| 24 | +- **Usage**: Django app can show "Offline" if VDI is offline (separate logic) |
| 25 | + |
| 26 | +### 4. VM Metadata Integration |
| 27 | +- **Content**: Base64-encoded lock file stored in VM metadata |
| 28 | +- **Purpose**: External access to VDI status without SSH access |
| 29 | +- **Command**: `gcloud compute instances describe <instance> --format="value(metadata.items[vdi-lock-content])" | base64 -d` |
| 30 | + |
| 31 | +### 5. Monitoring Service |
| 32 | +- **Service**: `vdi-monitor.service` (systemd) |
| 33 | +- **Script**: `/opt/vdi-setup/vdi-monitor.sh` |
| 34 | +- **Purpose**: Monitors lock file changes and triggers reconfiguration |
| 35 | +- **Bucket Sync**: Pulls latest files from GCS bucket before reconfiguring |
| 36 | + |
| 37 | +## Implementation Details |
| 38 | + |
| 39 | +### Lock File Structure |
| 40 | +```yaml |
| 41 | +vdi_setup_status: |
| 42 | + deployment_name: "vdi-test-scott" |
| 43 | + deployment_hash: "4d7bbe69940a9da1aae5b3acad02ad140e47a546d53111fb6e9b65e460d11aab" |
| 44 | + lock_version: "1.0" |
| 45 | + created_at: "2025-07-24T10:13:31Z" |
| 46 | + last_updated: "2025-07-24T10:13:31Z" |
| 47 | + force_rerun: false |
| 48 | + setup_status: "configuring" # configuring, available, error |
| 49 | + |
| 50 | + completed_roles: |
| 51 | + base_os: |
| 52 | + completed: false |
| 53 | + lock_manager: |
| 54 | + completed: true |
| 55 | + completed_at: "2025-07-24T10:13:31Z" |
| 56 | + secret_manager: |
| 57 | + completed: false |
| 58 | + user_provision: |
| 59 | + completed: false |
| 60 | + vnc: |
| 61 | + completed: false |
| 62 | + vdi_tool: |
| 63 | + completed: false |
| 64 | + |
| 65 | + user_secrets_status: |
| 66 | + last_secret_check: "2025-07-24T10:13:31Z" |
| 67 | + user_secrets_hash: "6a6a02d3d09933ca282ae2b02336343c9d73701e45bd81475fbd92dcf9d2f3b6" |
| 68 | + users_updated: |
| 69 | + - alice |
| 70 | + - bob |
| 71 | + |
| 72 | + user_management: |
| 73 | + current_users: |
| 74 | + - alice |
| 75 | + - bob |
| 76 | + previous_users: |
| 77 | + - alice |
| 78 | + - bob |
| 79 | + removed_users: [] |
| 80 | +``` |
| 81 | +
|
| 82 | +### Role Execution Pattern |
| 83 | +Each role follows this pattern: |
| 84 | +```yaml |
| 85 | +# Check if this role should run |
| 86 | +- name: Check if <role_name> role should run |
| 87 | + ansible.builtin.import_role: |
| 88 | + name: lock_manager |
| 89 | + tasks_from: check_lock |
| 90 | + vars: |
| 91 | + current_role: "<role_name>" |
| 92 | + |
| 93 | +# Skip all tasks if role should not run |
| 94 | +- name: Skip <role_name> tasks if role should not run |
| 95 | + ansible.builtin.debug: |
| 96 | + msg: "Skipping <role_name> role - already completed or not needed" |
| 97 | + when: not role_should_run |
| 98 | + |
| 99 | +# Role-specific tasks with conditional execution |
| 100 | +- name: Role task |
| 101 | + ansible.builtin.task: |
| 102 | + # task details |
| 103 | + when: role_should_run |
| 104 | + |
| 105 | +# Mark role as completed |
| 106 | +- name: Mark <role_name> role as completed |
| 107 | + ansible.builtin.import_role: |
| 108 | + name: lock_manager |
| 109 | + tasks_from: create_lock |
| 110 | + vars: |
| 111 | + current_role: "<role_name>" |
| 112 | + role_completed: true |
| 113 | + when: role_should_run |
| 114 | +``` |
| 115 | +
|
| 116 | +### Change Detection |
| 117 | +- **Deployment Hash**: SHA-256 hash of deployment configuration |
| 118 | +- **User Secrets Hash**: SHA-256 hash of user configuration (blueprint data only) |
| 119 | +- **Role Completion**: Individual tracking of each role's completion status |
| 120 | +
|
| 121 | +### Secret Manager Integration |
| 122 | +- **Blueprint-based Detection**: User secrets hash is calculated from blueprint data (usernames, ports, secret_names, etc.) |
| 123 | +- **Password Changes**: Changes to passwords in Secret Manager are **not automatically detected** |
| 124 | +- **Manual Re-initialization**: To apply password changes, use `force_rerun: true` in lock file |
| 125 | +- **Secret ID Changes**: Changes to `secret_name` references are automatically detected |
| 126 | + |
| 127 | +### Monitoring Service |
| 128 | +- **Trigger**: Lock file modification detected |
| 129 | +- **Action**: Sync files from GCS bucket and run Ansible reconfiguration |
| 130 | +- **Logging**: Detailed logs in `/var/log/vdi-monitor.log` |
| 131 | + |
| 132 | +## Benefits |
| 133 | + |
| 134 | +1. **Idempotency**: Safe to re-run deployment without conflicts |
| 135 | +2. **Efficiency**: Skip completed roles to reduce deployment time |
| 136 | +3. **Monitoring**: External visibility into VDI status |
| 137 | +4. **Automation**: Automatic reconfiguration on configuration changes |
| 138 | +5. **Reliability**: Proper error handling and status tracking |
| 139 | + |
| 140 | +## Usage |
| 141 | + |
| 142 | +### Manual Status Check |
| 143 | +```bash |
| 144 | +# Check on-disk lock file |
| 145 | +cat /opt/vdi-setup/.vdi-lock.yaml |
| 146 | +
|
| 147 | +# Check VM metadata |
| 148 | +gcloud compute instances describe <instance> --zone=<zone> --format="value(metadata.items[vdi-lock-content])" | base64 -d |
| 149 | +``` |
| 150 | + |
| 151 | +### Force Re-run |
| 152 | +```bash |
| 153 | +# Edit lock file to force re-run |
| 154 | +sed -i 's/force_rerun: false/force_rerun: true/' /opt/vdi-setup/.vdi-lock.yaml |
| 155 | +``` |
| 156 | + |
| 157 | +### Handle Secret Manager Password Changes |
| 158 | +```bash |
| 159 | +# To apply password changes from Secret Manager: |
| 160 | +# 1. Update the password in Secret Manager |
| 161 | +# 2. Force re-run to apply changes |
| 162 | +sed -i 's/force_rerun: false/force_rerun: true/' /opt/vdi-setup/.vdi-lock.yaml |
| 163 | +
|
| 164 | +# 3. Run the playbook again |
| 165 | +cd /tmp/vdi && ansible-playbook install.yaml --extra-vars @vars.yaml |
| 166 | +``` |
| 167 | + |
| 168 | +### Monitor Service |
| 169 | +```bash |
| 170 | +# Check service status |
| 171 | +systemctl status vdi-monitor |
| 172 | +
|
| 173 | +# View logs |
| 174 | +tail -f /var/log/vdi-monitor.log |
| 175 | +``` |
0 commit comments